# -*- coding: utf-8 -*-
"""
Shopify OAuth State 模型
处理OAuth状态管理，防止CSRF攻击，验证OAuth流程的完整性
"""

import hashlib
import secrets
import time
from typing import Optional, Dict, Any
import uuid
import datetime

from app.utils.db_utils import MySQLHelper
from app.conf.config import config, logger


class ShopifyOAuthState:
    """
    Shopify OAuth 状态管理类
    负责创建、验证和清理OAuth状态
    """
    
    TABLE_NAME = 'shopify_oauth_states'
    
    def __init__(self):
        """初始化OAuth状态管理器"""
        self.db = MySQLHelper()
        self.logger = logger
    
    def generate_state(self, shop_domain: str) -> Dict[str, Any]:
        """
        生成OAuth状态
        
        Args:
            shop_domain: 店铺域名
            
        Returns:
            Dict: 包含state字符串、shop_domain和created_at的字典
        """
        try:
            # 验证店铺域名格式
            if not shop_domain or not shop_domain.endswith('.myshopify.com'):
                raise ValueError(f"无效的店铺域名: {shop_domain}")
            
            # 规范化店铺域名（转换为小写）
            shop_domain = shop_domain.lower().strip()
            
            # 生成随机state
            random_bytes = secrets.token_bytes(32)
            state = hashlib.sha256(random_bytes).hexdigest()
            
            # 生成唯一标识符
            nonce = str(uuid.uuid4())
            
            # 当前时间戳
            created_at = datetime.datetime.now()
            expires_at = created_at + datetime.timedelta(seconds=config.OAUTH_STATE_EXPIRY)
            
            # 保存到数据库
            data = {
                'state': state,
                'shop_domain': shop_domain,
                'nonce': nonce,
                'created_at': created_at,
                'expires_at': expires_at,
                'is_used': 0
            }
            
            # 自动清理过期的状态记录
            self._clean_expired_states()
            
            # 保存新状态
            self.db.insert(self.TABLE_NAME, data)
            
            self.logger.info(f"为店铺 {shop_domain} 生成OAuth状态")
            
            return {
                'state': state,
                'shop_domain': shop_domain,
                'created_at': created_at,
                'expires_at': expires_at
            }
            
        except Exception as e:
            self.logger.error(f"生成OAuth状态失败: {str(e)}")
            raise
    
    def validate_state(self, state: str, shop_domain: str) -> bool:
        """
        验证OAuth状态是否有效
        
        Args:
            state: 要验证的state字符串
            shop_domain: 店铺域名
            
        Returns:
            bool: 状态有效返回True，否则返回False
        """
        try:
            if not state or not shop_domain:
                self.logger.warning("状态验证失败: state或shop_domain为空")
                return False
            
            # 规范化店铺域名
            shop_domain = shop_domain.lower().strip()
            
            # 查询状态记录
            where = {
                'state': state,
                'shop_domain': shop_domain,
                'is_used': 0
            }
            
            result = self.db.select(self.TABLE_NAME, where=where)
            
            if not result or len(result) != 1:
                self.logger.warning(f"状态验证失败: 找不到有效的状态记录，shop_domain: {shop_domain}")
                return False
            
            state_record = result[0]
            
            # 检查是否过期
            expires_at = state_record.get('expires_at')
            if isinstance(expires_at, str):
                expires_at = datetime.datetime.strptime(expires_at, "%Y-%m-%d %H:%M:%S")
            
            if datetime.datetime.now() > expires_at:
                self.logger.warning(f"状态验证失败: 状态已过期，shop_domain: {shop_domain}")
                # 标记为已使用
                self._mark_as_used(state)
                return False
            
            # 验证成功后标记为已使用
            self._mark_as_used(state)
            
            self.logger.info(f"状态验证成功，shop_domain: {shop_domain}")
            return True
            
        except Exception as e:
            self.logger.error(f"验证OAuth状态失败: {str(e)}")
            return False
    
    def _mark_as_used(self, state: str) -> bool:
        """
        将状态标记为已使用
        
        Args:
            state: 状态字符串
            
        Returns:
            bool: 操作成功返回True
        """
        try:
            data = {
                'is_used': 1,
                'used_at': datetime.datetime.now()
            }
            
            where = {'state': state}
            
            affected_rows = self.db.update(self.TABLE_NAME, data, where)
            
            return affected_rows > 0
            
        except Exception as e:
            self.logger.error(f"标记状态为已使用失败: {str(e)}")
            return False
    
    def _clean_expired_states(self) -> int:
        """
        清理过期的状态记录
        
        Returns:
            int: 清理的记录数量
        """
        try:
            current_time = datetime.datetime.now()
            
            # 删除所有过期且未使用的状态，或者已使用超过24小时的状态
            sql = f"""
            DELETE FROM {self.TABLE_NAME} 
            WHERE (is_used = 0 AND expires_at < %s) 
               OR (is_used = 1 AND used_at IS NOT NULL AND used_at < %s)
            """
            
            params = (current_time, current_time - datetime.timedelta(days=1))
            
            affected_rows = self.db.execute_sql(sql, params)
            
            if affected_rows > 0:
                self.logger.debug(f"清理了 {affected_rows} 条过期的OAuth状态记录")
            
            return affected_rows
            
        except Exception as e:
            self.logger.error(f"清理过期状态记录失败: {str(e)}")
            return 0
    
    def get_state_info(self, state: str) -> Optional[Dict[str, Any]]:
        """
        获取状态信息
        
        Args:
            state: 状态字符串
            
        Returns:
            Dict or None: 状态信息字典，不存在返回None
        """
        try:
            where = {'state': state}
            result = self.db.select(self.TABLE_NAME, where=where)
            
            if result and len(result) > 0:
                return result[0]
            
            return None
            
        except Exception as e:
            self.logger.error(f"获取状态信息失败: {str(e)}")
            return None
    
    def delete_state(self, state: str) -> bool:
        """
        删除指定的状态记录
        
        Args:
            state: 状态字符串
            
        Returns:
            bool: 删除成功返回True
        """
        try:
            where = {'state': state}
            affected_rows = self.db.delete(self.TABLE_NAME, where)
            
            return affected_rows > 0
            
        except Exception as e:
            self.logger.error(f"删除状态记录失败: {str(e)}")
            return False


# 创建全局实例
oauth_state_manager = ShopifyOAuthState()